/**
 * 
 */
package it.codin.progettoX;

import java.util.ArrayList;
import java.util.HashMap;

import weka.core.Attribute;
import weka.core.Instances;
import weka.core.Instance;
import weka.core.matrix.Matrix;

/**
 * @author gnocco
 *
 */

public class Core {
	private Instances dataset = null;
	private HashMap<Coppia,Double> correlazioni = new HashMap<Coppia,Double>();
	
	/*
	 * Costruttore della classe Core
	 * 
	 * @param dataset il dataset.
	 */
	public Core (Instances dataset){
		this.dataset = dataset;
	}
	
	/*
	 * Metodo che svolge il lavoro
	 *
	 * @param k dimensione delle feature
	 */
	public ArrayList<Attribute> mRMR(int k){
		ArrayList<Attribute> S = new ArrayList<Attribute>();
		for(int i = 0; i < k; i++){
			S.add(findMigliore());
		}
		return S;
	}
	
	/*
	 * Metodo che restituisce la miglior feature disponibile
	 * 
	 * @return x - L'attributo che massimizza la condizione mRMR. 
	 */
	public Attribute findMigliore(){
		Attribute appo = null;
		double max = 0.0;
		for(int i = 0; i < dataset.numAttributes(); i++){
			Attribute att = dataset.attribute(i);
			double attuale = calcolo(att);
			if(max < attuale){
				appo = att;
				max = attuale;
			}
		}
		return appo;
	}	

	private double calcolo(Attribute att){
		return 0.0;
	}
	
	private double p(double[] z, Attribute x, Attribute y){
		double somma = 0.0;
		int num = 0;
		for(Instance in : dataset){
			if(!in.isMissing(x) && !in.isMissing(y)){
				num++;
				double[] appo = new double[2];
				appo[0] = z[0] - in.value(x);
				appo[1] = z[1] - in.value(y);
				somma += delta(appo,x,y);
			}
		}
		return somma/num;
	}

	private double p(double z, Attribute x){
		double somma = 0.0;
		int num = 0;
		for(Instance in : dataset){
			if(!in.isMissing(x)){
				num++;
				double appo = z - in.value(x);
				somma += delta(appo,x);
			}
		}
		return somma/num;
	}
	
	
	private double delta(double[] z, Attribute x, Attribute y){
		double[][] A = new double[2][2];
		A[0][0] = covariance(x,x);
		A[0][1] = covariance(x,y);
		A[1][0] = A[0][1];
		A[1][1] = covariance(y,y);
		Matrix M = new Matrix(A);
		Matrix Mm1 = M.inverse();
		double result = Math.exp(-(( z[0]*(z[0]*Mm1.get(0, 0) + z[1]*Mm1.get(1, 0)) + z[1]*(z[0]*Mm1.get(0, 1) + z[1]*Mm1.get(1, 1))))/2);
		result /= (2 * Math.PI) * Math.sqrt(Mm1.normInf());
		return result;
	}

	private double delta(double z, Attribute x){
		double M = covariance(x,x);
		double result = Math.exp(-(M*z*z)/2);
		result /= (2 * Math.PI) * Math.sqrt(M);
		return result;
	}
	
	private double covariance(Attribute x, Attribute y){
		Coppia c = new Coppia(x,y);
		double result = 0.0;
		if(correlazioni.containsKey(c)){
			result = correlazioni.get(c);
		} else {
			double ex = 0.0;
			double ey = 0.0;
			double exy = 0.0;
			int num = 0;
			for(Instance in : dataset){
				if(!in.isMissing(x) && !in.isMissing(y)){
					num++;
					ex += in.value(x);
					ey += in.value(y);
					exy += in.value(x) * in.value(y);
				}
			}
			result = (exy/num) - ((ex/num)*(ey/num));
			correlazioni.put(c, result);
		}
		return result;
	}

	private class Coppia {
		Attribute x = null;
		Attribute y = null;
		public Coppia(Attribute x, Attribute y){
			this.x = x;
			this.y = y;
		}
		public int hashCode(){
			return x.hashCode() + y.hashCode();
		}
		public boolean equals(Object o){
			boolean result = false;
			try{
				Coppia c = (Coppia)o;
				if((c.x.equals(x) & c.y.equals(y)) || (c.x.equals(y) & c.y.equals(x)))
					result = true;
			} catch (Exception e){
				
			}
			return result;
		}
	}




}
